About the Plugin-API

TMLG has a plugin interface for audio-manipulation and/or visualisation. The interface can be used for VU-metres, FFT-display, equalizers and the likes. From within the settings in TMLG one can load/remove these plugins much like we expect to control a commodity like TMLG from within the "exchange" program. As an example I've included the "SampleView.plugin".


How is it organized

A TMLG-plugin is in essence only a library with some special startupcode, allowing TMLG to recognize it. The interface makes it possible to construct visual plugins or alter the sound before playback. The interface offers a common messageport for use in a plugins windows, if any. Two signals is also provided for use in the plugin when quitting subtasks. These signals are garantied to be there, but are not to be used in asyncronous communication, that is the plugin won't respond to these signals if not explicitly "wait"ed for inside one of the functions. The following is an explanation of how to construct a plugin, and it assumes that you already know how to code a library. In short these are the steps to go through to make a plugin (in StormC):
  • Setup a new library-project
  • Create the special startup-code, allowing TMLG to recognize the library as a plugin.
  • Make all the functions of the library. Each explained below.
  • Compile the project.
  • Try the new plugin from within TMLG... (repeat last three steps until satisfied)
It doesn't seem that hard does it? I, draco, don't think so... Anyway, I have included the sourcecode for "sampleview.plugin" as an example. It's a bit special, as it creates a new task for updating the display. It is not very nice structured or anything, as it is something I've developed along the way just to test the interface, but it should give an idea as of how to make a plugin.

The startup-code

When TMLG opens the plugin-library, it expects the plugin to "announce" itself. If this part is missing or fails, an error message displaying "not a plugin" will appear. When the plugin is loaded by the system, it must search for the public semaphore named "TMLG_PLUGIN_SEM" and lock it! The semaphore is the head of a datastructure as defined in "AudioPluginStructs.h".
struct PluginSemaphore {
	SignalSemaphore Sem;
	/* WRITE during OpenLibrary */
	BOOL IsPlugin;      /* used to distinguish between library and plugin */
	ULONG PluginVer;
	ULONG PluginRev;
	char *PluginName;
	char *PluginCopyRight;
	ULONG PluginFlags;

	/* READ ONLY! */
	ULONG PluginSignal;     /* free signal for use from library-side of a plugin (when quitting sub-tasks)*/
	ULONG PluginSignal2;    /* free signal for use from library-side of a plugin (when quitting sub-tasks)*/
	ULONG TMLGVer;
	ULONG TMLGRev;
};

Part of this structure must be filled by the plugin upon startup! It is almost self explanatory, but here's a short description:
  • IsPlugin
    Set this one to true. TMLG checks if this one is true after loading the plugin (library).
  • PluginVer
    Specify the version of the plugin.
  • PluginRev
    The revision number.
  • PluginName
    Pointer to the name of the plugin.
  • PluginCopyRight
    Plugin copyright message.
  • PluginFlags
    These flags tells TMLG what the plugin is for!
    #define PLUGIN_HAS_PREFS 1      /* This plugin has a preference window */
    #define PLUGIN_HAS_ABOUT 2      /* I've got my own about window */
    #define PLUGIN_HAS_WINDOW 4     /* This plugin has a "visual" window */
    #define PLUGIN_DISPLAYS   4
    #define PLUGIN_TRANSFORMS 8     /* I'm altering the sound */
    

    Or the flags together to get the right attributes. If the plugin doesn't have an about window, a default window will be used together with the version/name/copyright information specified in the structure.

The library funtions

First a short scenarion, descriping the use and context of the different functions. At first, immediately after loading the plugin, TMLG will try to initialize it. This is done using the function "InitPlugin()". This function returns a pointer to a private datastructure or NULL. Now TMLG will try to open the plugin visual-window if such one exists (PLUGIN_HAS_WINDOW or PLUGIN_DISPLAYS), calling "ShowPlugin". Now we're ready to go! When starting on a new song, TMLG calls the function "InitTransformation" and at the end the function "EndTransformation" is called. In between TMLG will call the functions "Transform" and/or "PluginDisplay" with the newly decoded sounddata or "about to be played" data! When quitting TMLG will call "QuitPlugin"...

APTR InitPlugin(PluginData *data, MsgPort *windowport, CxObj *broker);

Initialize the plugin befor usage. If this one returns NULL, a requester will appear saying the plugin couldn't be initialized.
Input
  • data
    Pointer to previously saved data or NULL. See GetPluginData.
  • windowport
    Pointer to a shared msgport for the plugins windows. HandleWindowMsg will be called each time a message arrives at this port.
  • broker
    Pointer to TMLG's commodity broker. You may add hotkeys and the likes to this, just be shure they have a unique id, and remember to remove it during QuitPlugin.
Output
Returns pointer to plugin private data. This pointer is passed as argument to every other function. NULL indicates failure to initialize the plugin.

void QuitPlugin(APTR data);

Cleanup before unloading the plugin.
Input
  • data
    Pointer to plugin private data, as returned by InitPlugin.
Output
Nothing.

BOOL ShowPlugin(APTR data, MsgPort *port); Please reveal the plugins "visual" window.

Input
  • data
    Pointer to plugin private data, as returned by InitPlugin.
  • port
    Pointer to shared MsgPort for usage as the windows UserPort. (win->UserPort = port;)
Output
Return TRUE if windows has opened. Otherwise FALSE.

BOOL AboutPlugin(APTR data, MsgPort *port);

Show a plugin specific "about" window if any (PLUGIN_HAS_ABOUT). If this cannot be done, TMLG uses a default window, displaying the information given at startup.
Input
  • data
    Pointer to plugin private data, as returned by InitPlugin.
  • port
    Pointer to shared MsgPort for usage as the windows UserPort. (win->UserPort = port;)
Output
Return TRUE if windows has opened. Otherwise FALSE.

BOOL PluginPrefs(APTR data, MsgPort *port);

Open the plugins preference window, if any (PLUGIN_HAS_PREFS)
Input
  • data
    Pointer to plugin private data, as returned by InitPlugin.
  • port
    Pointer to shared MsgPort for usage as the windows UserPort. (win->UserPort = port;)
Output
Return TRUE if windows has opened. Otherwise FALSE.

void HidePlugin(APTR data);

Please hide the plugins windows.
Input
  • data
    Pointer to plugin private data, as returned by InitPlugin.
Output
Nothing.

BOOL HiddenPlugin(APTR data);

Is the plugin window hidden?
Input
  • data
    Pointer to plugin private data, as returned by InitPlugin.
Output
Return TRUE if the plugin has no open window, otherwise FALSE.

BOOL HandleWindowMsg(APTR data, IntuiMessage **msgptr);

Each time a message arrives at the shared messageport, this function will be called until either there are no more plugins, or one of the functions has returned TRUE. It is allowed to reply the message (must be done before closing a window!), just remember to set the IntuiMessage pointer to NULL in this case (*msgptr = NULL;). Remember this message might not be for your plugin!
Input
  • data
    Pointer to plugin private data, as returned by InitPlugin.
  • msgptr
    Pointer to the IntuiMessage pointer!
Output
Return TRUE if you answered to this window. Otherwise FALSE.

BOOL PluginHandleKey(APTR data, ULONG key);

If TMLG recieves a commodity message it doesn't know, it's id (key) is passed to this function in every plugin until no more plugins exists, or one has returned TRUE.
Input
  • data
    Pointer to plugin private data, as returned by InitPlugin.
  • key
    The commodity message id.
Output
Return TRUE if you answered to this key. Otherwise FALSE.

void InitTransformation(APTR data, char *label, ULONG duration, ULONG samplefreq, ULONG channels, ULONG bitrate, ULONG elapsed);

Prepare plugin to recieve audiodata (Start Of Song).
Input
  • data
    Pointer to plugin private data, as returned by InitPlugin.
  • label
    Song label.
  • duration
    Duration of the song in seconds.
  • samplefreq
    Sounddata sample frequency.
  • channels
    Number of channels used for playback.
  • bitrate
    The used bitrate.
  • elapsed
    The time in seconds already elapsed before playback.
Output
Nothing.

void Transform(APTR data, WORD *left_buffer, WORD *right_buffer, ULONG samples);

Function called for altering audiodata. This is before the data enters TMLG's internal ringbuffer.
Input
  • data
    Pointer to plugin private data, as returned by InitPlugin.
  • left_buffer
    Pointer to audiobuffer for left channel.
  • right_buffer
    Pointer to audiobuffer for right channel. This may be NULL in case of mono playback.
  • samples
    The number of samples in the audiobuffer.
Output
Nothing.

void PluginDisplay(APTR data, WORD *left_buffer, WORD *right_buffer, ULONG samples);

Function called immediately before playback. Used for the "visual" plugins.
Input
  • data
    Pointer to plugin private data, as returned by InitPlugin.
  • left_buffer
    Pointer to audiobuffer for left channel.
  • right_buffer
    Pointer to audiobuffer for right channel. This may be NULL in case of mono playback.
  • samples
    The number of samples in the audiobuffer.
Output
Nothing.

void EndTransformation(APTR data);

Cleanup after last recieved sounddata (End Of Song).
Input
  • data
    Pointer to plugin private data, as returned by InitPlugin.
Output
Nothing.

struct PluginData *GetPluginData(APTR data);

Return pointer to a well defined structure for saving on disk.
struct PluginData {
	ULONG Size;         /* size of the structure (the number of bytes to be saved!)
	BOOL  ShowMain;     /* Is the "visual" window open?
	UWORD MainX, MainY, MainW, MainH;   /* coordinates of the "visual" window */
};

Note: this is the minimum structure, if your plugin doesn't have a window, set ShowMain to FALSE.
Input
  • data
    Pointer to plugin private data, as returned by InitPlugin.
Output
Return pointer to the data that should be saved.

void LockPluginGUI(APTR data);

Please lock the plugins windows for further input.
Input
  • data
    Pointer to plugin private data, as returned by InitPlugin.
Output
Nothing.

void UnLockPluginGUI(APTR data);

Unlock the plugins windows.
Input
  • data
    Pointer to plugin private data, as returned by InitPlugin.
Output
Nothing.

void PluginPriority(APTR data, LONG pri);

Please change the priority of any subtasks the plugin may have.
  • data
    Pointer to plugin private data, as returned by InitPlugin.
  • pri
    The intended new priority.
Output
Nothing.


Any comments or questions can be e-mailed to me, draco at , or you can reach me on irc #AmigaDK...